//JSHint options
/*global Highcharts, Spinner*/

/*
 * Fix missing indexOf in IE8
 */
if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function(elt /*, from*/) {
         "use strict";

        var len  = this.length >>> 0,
            from = Number(arguments[1]) || 0;

        from = (from < 0) ? Math.ceil(from) : Math.floor(from);

        if (from < 0) {
            from += len;
        }

        for (; from < len; from++) {
            if (from in this && this[from] === elt) {
                return from;
            }
        }

        return -1;
    };
}

function createCookie(name, value, days) {
    if (days) {
        var date = new Date();
        date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
        var expires = "; expires=" + date.toGMTString();
    } else var expires = "";
    document.cookie = escape(name) + "=" + escape(value) + expires + "; path=/";
}

function readCookie(name) {
    var nameEQ = escape(name) + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ' ') c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) == 0) return unescape(c.substring(nameEQ.length, c.length));
    }
    return null;
}

function eraseCookie(name) {
    createCookie(name, "", -1);
}

var body_scrolled = false;
$(window).scroll(function() {
        body_scrolled = true;
});

function get_unit_name(series) {
    var unit = HARSTORAGE.Units[series.name];
    if (unit) { return unit; };
    if (series.linkedParent) { return HARSTORAGE.Units[series.linkedParent.name]; };
    return "";
};


/*
 * Name space
 */
var HARSTORAGE = HARSTORAGE || {};

/*
 * Time metrics
 */
HARSTORAGE.times = [
    "Full Load Time",
    "onLoad Event",
    "Start Render Time",
    "Time to First Byte"
];

/*
 * Units
 */
HARSTORAGE.Units = {
    "Full Load Time": "s",
    "Transaction Time": "ms",
    "Total Requests": "",
    "Total Size": "kB",
    "Page Speed Score": "",
    "onLoad Event": "s",
    "Start Render Time": "s",
    "Time to First Byte": "s",
    "Total DNS Time": "ms",
    "Total Transfer Time": "ms",
    "Total Server Time": "ms",
    "Avg. Connecting Time": "ms",
    "Avg. Blocking Time": "ms",
    "Text Size": "kB",
    "Media Size": "kB",
    "Cache Size": "kB",
    "Redirects": "",
    "Bad Rquests": "",
    "Domains":  ""
};

/*
 * Data Converter
 */
HARSTORAGE.Converter = function(points) {
    "use strict";

    // Series data
    var splitResults = points.split(";"),
        numberOfSets = splitResults.length - 2,
        dataArray = [],
        minmaxArray = [];

//    numberOfSets = 2;


    // Labels and Timestamps
    var labels = splitResults[0].split("#"),
        categories = splitResults[1].split("#"),
        numberOfPoints = categories.length,
        pointTriple,
        pointValue;

    for (var dataSetIndex = 0; dataSetIndex < numberOfSets; dataSetIndex += 1 ) {
        var dataSet = splitResults[dataSetIndex + 2].split("#");
        dataArray.push(dataSet);
        minmaxArray.push(new Array(dataSet.length));

        // Convert string values to numbers
        for (var pointIndex = 0; pointIndex < numberOfPoints; pointIndex += 1 ) {
            // Original Value or Triple
            pointTriple = dataArray[dataSetIndex][pointIndex].split("|");
            pointValue = pointTriple[0];
            if (pointTriple.length == 3) {
                if (pointTriple[1] == "n/a" || pointTriple[2] == "n/a") {
                    minmaxArray[dataSetIndex][pointIndex] = [null, null];
                } else {
                    if (HARSTORAGE.times.indexOf(labels[dataSetIndex]) !== -1) {
                        minmaxArray[dataSetIndex][pointIndex] = [parseFloat(pointTriple[1]) / 1000,
                                                                 parseFloat(pointTriple[2]) / 1000];
                    } else {
                        minmaxArray[dataSetIndex][pointIndex] = [parseFloat(pointTriple[1]),
                                                                 parseFloat(pointTriple[2])];
                    };
                };
            } else {
                minmaxArray[dataSetIndex][pointIndex] = [null, null];
            };

            if (pointValue == "None") {
                pointValue = null;
            } else if (HARSTORAGE.times.indexOf(labels[dataSetIndex]) !== -1) {
                // Parsed value
                pointValue = parseFloat(pointValue / 1000, 10);
                // Rounded value
                if (pointValue > 1){
                    pointValue = Math.round(pointValue * 10) / 10;
                }
            } else {
                // Parsed value
                pointValue = parseInt(pointValue, 10);
            }

            dataArray[dataSetIndex][pointIndex] = pointValue;
        }
    }

    // Colors for Y Axis labels
    var colors = HARSTORAGE.Colors();

    // Y Axis and series
    var yAxis = [],
        series = [];

    for (dataSetIndex = 0; dataSetIndex < numberOfSets; dataSetIndex += 1) {
        yAxis.push({
            title: {
                text: labels[dataSetIndex],
                style: {
                    color: colors[dataSetIndex]
                }
            },
            min: 0,
            opposite: (dataSetIndex%2 === 0) ? false : true,
            showEmpty: false
        });

        series.push({
            id: "series-" + dataSetIndex,
            color: colors[dataSetIndex],
            name: labels[dataSetIndex],
            yAxis: dataSetIndex,
            data: dataArray[dataSetIndex]
        });
    };

    for (dataSetIndex = 0; dataSetIndex < numberOfSets; dataSetIndex += 1) {
        series.push({
            id: "error-" + dataSetIndex,
            linkedTo: "series-" + dataSetIndex,
            type: "errorbar",
            color: colors[dataSetIndex],
            yAxis: dataSetIndex,
            data: minmaxArray[dataSetIndex]
        });
    }

    return {
        "categories": categories,
        "yAxis": yAxis,
        "series": series
    };
};

/*
 * Timeline chart
 */
HARSTORAGE.Timeline = function(run_info) {
    "use strict";
    var that = this;

    this.run_info = run_info;
    this.block_date_trigger = false;
    this.navi_history = [];
    this.multi_har_idx = null;
    this.chart = null;


    // the visible data by default:
    try {
        this.visible_metrics = JSON.parse(readCookie("visible_metrics"));
    } catch (e) {
        this.visible_metrics = null;
    };

    if (!this.visible_metrics) {
        this.visible_metrics = {"Full Load Time": true,
                                "Time to First Byte": true,
                                "Total Server Time": true}
    };

    // Add event handler to datepicker
    var from_date = document.getElementById("datepicker_start");
    var to_date = document.getElementById("datepicker_end");

    from_date.onchange = function() {
        if (that.block_date_trigger) return;
        that.get(that.run_info.series);
    };

    to_date.onchange = function() {
        if (that.block_date_trigger) return;
        that.get(that.run_info.series);
    };

    $("#navi_back").click(function() {
        if (that.navi_history.length <= 1) return;
        that.navi_history.pop(); // thats the current one
        var back_uri = that.navi_history.pop(); // the one before
        that.get(undefined, undefined, undefined, back_uri);
    });

    $("#navi_15min").click(function() {
        that.get(that.run_info.series, undefined, undefined, "15min");
    });

    $("#navi_1h").click(function() {
        that.get(that.run_info.series, undefined, undefined, "1h");
    });

    $("#navi_1d").click(function() {
        that.get(that.run_info.series, undefined, undefined, "1d");
    });

    $("#navi_1w").click(function() {
        that.get(that.run_info.series, undefined, undefined, "1w");
    });

    $("#navi_all").click(function() {
        that.get(that.run_info.series, undefined, undefined, "all");
    });

    $("#navi_reload").click(function() {
        var current_uri = that.navi_history[that.navi_history.length - 1]; // the current one
        that.get(undefined, undefined, undefined, current_uri);
    });

    $("#histo").click(function() {
        var current_uri = that.navi_history[that.navi_history.length - 1]; // the current one
        var url = current_uri.replace("timeline?", "/superposed/histogram?") + "&metric=full_load_time"
        var w = window.open(url, "histogram", "width=1260,height=550")
    });


};

// Get data for timeline
HARSTORAGE.Timeline.prototype.get = function(series, zoom, force_uri, navi, multi_har_idx) {
    "use strict";

    // Show Ajax spinner
    $("#spinner").show();

    $("#run_info_box_empty").show();
    $("#run_info_box").hide();
    $("#run_timestamp").empty().trigger("liszt:updated");
    this.run_info.visible = false;

    // Pointer
    var that = this;

    // Retrieve data for timeline via XHR call
    var xhr = new XMLHttpRequest();
    var uri = null;


    xhr.onreadystatechange = function() {
        if (this.readyState === 4 && this.status === 200) {
            if (this.responseText != "no-data") {
                var current_uri = that.navi_history[that.navi_history.length - 1]; // the current one
                if (current_uri != uri) {
                    that.navi_history.push(uri);
                };

                var rc = this.responseText.split("$");
                var points = rc[0];
                var new_from = rc[1];
                var new_to = rc[2];
                that.ag_timestamps = rc[3].split(";");
                that.ag_timestamps = rc[3].split(";");
                if (rc[4]) {
                    var multi_har_info = rc[4].split("|");
                } else {
                    var multi_har_info = null;
                };

                if (multi_har_info && multi_har_idx == null) {
                    $("#run_info_box_empty").text("Please select a screenshot from the click-path.");
                } else {
                    $("#run_info_box_empty").text("Please select a data-point from the diagramm.");
                };

                that.block_date_trigger = true;
                $("#datepicker_start").val(new_from);
                $("#datepicker_end").val(new_to);
                that.block_date_trigger = false;

                that.draw(points, multi_har_info, multi_har_idx);
            } else {
                // display empty chart
                $("#timeline").html("<h3>No data available...</h3>");
                $("#multi_har_info").html("");
            };
            // Show Ajax spinner
            $("#spinner").fadeOut();
        }


    };

    if (typeof(force_uri) == "undefined") {
        var from_ts = document.getElementById("datepicker_start").value;
        var end_ts = document.getElementById("datepicker_end").value;

        uri = "timeline?series=" + encodeURIComponent(series) + "&from=" + from_ts + "&to=" + end_ts;

        if (typeof(navi) != "undefined") {
            uri += "&navi=" + navi;
        };

        if (typeof(zoom) != "undefined") {
            uri += "&zoom_min=" + encodeURI(zoom["min"]);
            uri += "&zoom_max=" + encodeURI(zoom["max"]);
        };

        if (typeof(multi_har_idx) != "undefined") {
            if (multi_har_idx == null) {
                that.multi_har_idx = null;
            } else {
                uri += "&mhidx=" + multi_har_idx;
                that.multi_har_idx = multi_har_idx;
            };
        } else if (that.multi_har_idx != null) {
            uri += "&mhidx=" + that.multi_har_idx;
        };
    } else {
        var uri = force_uri;
    };

    setTimeout(function() {
        xhr.open("GET", uri, true);
        xhr.send();
    }, 100);
};

// Get data for timeline
HARSTORAGE.Timeline.prototype.on_selection = function(min, max) {
    this.get(this.run_info.series,
             {"min": min, "max": max});
}


// Draw timeline
HARSTORAGE.Timeline.prototype.draw = function(points, multi_har_info, multi_har_idx) {
    "use strict";

    // Pointer
    var that = this;

    // Convert data from custom format to arrays for chart
    var converter = HARSTORAGE.Converter(points);

    var categories = converter.categories,
        yAxis = converter.yAxis,
        series = converter.series;

    for (var idx = 0; idx < series.length; idx++) {
        if (series[idx].linkedTo) {
            var name = series[parseInt(series[idx].linkedTo.substr(7))].name;
        } else {
            var name = series[idx].name;
        };
        if (this.visible_metrics[name]) {
            series[idx].visible = true;
        } else {
            series[idx].visible = false;
        };
    };


    var chart = new Highcharts.Chart({
        chart: {
            renderTo: "timeline",
            zoomType: "x",
            defaultSeriesType: "spline",
            animation: false,
            events: {selection: function(event) {
                                    var min_idx = parseInt(event.xAxis[0].min);
                                    var max_idx = parseInt(event.xAxis[0].max);
                                    var min_value = event.xAxis[0].axis.categories[min_idx];
                                    if (max_idx < event.xAxis[0].axis.categories.length - 1) {
                                        max_idx += 1;
                                    };
                                    var max_value = event.xAxis[0].axis.categories[max_idx];
                                    that.on_selection.call(that, min_value, max_value);
                                    event.preventDefault();
                                 }
            },

        },
        credits: {
            enabled: false
        },
        exporting: {
            url: "/chart/export",
            filename: "timeline",
            width: 1200
        },
        title: {
            text: this.run_info.label
        },
        xAxis: [{
            categories: categories,
            tickInterval: Math.ceil(categories.length / 10),
            tickmarkPlacement: "on"
        }],
        yAxis: yAxis,
        tooltip: {
            formatter: function() {
                var unit = get_unit_name(this.series);
                return "<b>" + this.y + " " + unit + "</b>" + " (" + this.x + ")";
            }
        },
        plotOptions: {
            line: { connectNulls: true},
            series: {
                marker: {
                    enabled: true,
                    radius: 3,
                    states: {
                        hover: {
                            radius: 6
                        }
                    }
                },

                animation: false,
                cursor: "pointer",
                shadow: false,
                events: {
                    hide: function(event) {
                        delete that.visible_metrics[this.yAxis.options.title.text];
                        createCookie("visible_metrics", JSON.stringify(that.visible_metrics), 14);
                    },
                    show: function(event) {
                        that.visible_metrics[this.yAxis.options.title.text] = true;
                        createCookie("visible_metrics", JSON.stringify(that.visible_metrics), 14);
                    }
                },
                point: {
                    events: {
                        click: function() {
                            var ts = this.series.points;
                            for (var idx = 0; idx < ts.length; idx++) {
                                if (ts[idx].category == this.category) {

                                    chart.xAxis[0].removePlotLine("sel-plot-line");
                                    chart.xAxis[0].addPlotLine({
                                        value: idx,
                                        color: "red",
                                        width: 2,
                                        id: "sel-plot-line"});

                                    if (multi_har_info) {
                                        // update multi-har
                                        if (that.ag_timestamps[idx]) {
                                            that.run_info.get_multi_har_info(that.run_info.series, undefined, that.ag_timestamps[idx], multi_har_idx);
                                        } else {
                                            that.run_info.get_multi_har_info(that.run_info.series, this.category, undefined, multi_har_idx);
                                        };
                                    };
                                    if ((multi_har_info && multi_har_idx != null) || !multi_har_info) {
                                        // update run-info
                                        if (that.ag_timestamps[idx]) {
                                            that.run_info.get(that.run_info.series, undefined, that.ag_timestamps[idx], multi_har_idx); // this point is aggregated
                                        } else {
                                            that.run_info.get(that.run_info.series, this.category, undefined, multi_har_idx); // a single-value point
                                        };
                                    };
                                    return;
                                };
                            };
                        }
                    }
                }
            }
        },
        series: series
    });
    this.chart = chart;


    if (that.run_info.json) {
        that.set_marker(that.run_info.json["timestamp"]);
    };

    if (multi_har_idx == null) {
        that.run_info.draw_multi_har_info(multi_har_info, multi_har_idx);
    };

};

HARSTORAGE.Timeline.prototype.set_marker = function(ts) {
    if (!this.chart) return;

    this.chart.xAxis[0].removePlotLine("sel-plot-line");

    var sel_idx = null;
    var data = this.chart.series[0]["data"];
    for (var idx = 0; (idx < data.length - 1); idx++) {
        var point_ts1 = data[idx].category;
        var point_ts2 = data[idx + 1].category;
        if (point_ts1 == ts) {
            sel_idx = idx;
            break;
        } else if (point_ts1 < ts && point_ts2 > ts) {
            sel_idx = idx;
            break;
        } else if (point_ts2 == ts) {
            sel_idx = idx + 1;
            break;
        };
    };

    if (sel_idx != null) {
        this.chart.xAxis[0].addPlotLine({
                                            value: sel_idx,
                                            color: "red",
                                            width: 2,
                                            id: "sel-plot-line"
                                        });
    };

};


/*
 * Histogram Chart
 */
HARSTORAGE.Histogram = function() {
    "use strict";
};

HARSTORAGE.Histogram.prototype.draw = function(points, title) {
    "use strict";

    // Series data
    var splitResults = points.split(";"),
        yAxis  = [],
        series = [];

    series = splitResults[0].split("#");

    var temp_array = splitResults[1].split("#");

    for (var i = 0, l = temp_array.length; i < l; i += 1 ) {
        yAxis.push(parseFloat(temp_array[i], 10));
    }

    // Colors for Y Axis labels
    var color = HARSTORAGE.Colors()[0];

    // Chart Object
    new Highcharts.Chart({
        chart: {
            renderTo: "chart",
            defaultSeriesType: "column"
        },
        credits: {
            enabled: false
        },
        title: {
            text: title + " (" + HARSTORAGE.Units[title] + ")"
        },
        legend: {
            enabled: false
        },
        plotOptions: {
            series: {
                cursor: "pointer"
            }
        },
        xAxis: [{
            categories: series
        }],
        yAxis: [{
            title: {
                text: "Percentage of Total",
                style: {
                    color: color
                }
            },
            min: 0
        }],
        tooltip: {
            formatter: function() {
                var unit = HARSTORAGE.Units[title];
                return "<b>" + this.y + " %</b>" + " (" + this.x + " " + unit + ")";
            }
        },
        series: [{
            data: yAxis
        }]
    });
};

/*
 * Column Chart
 */
HARSTORAGE.Columns = function() {
    "use strict";
};

HARSTORAGE.Columns.prototype.draw = function(points, chart_type) {
    "use strict";

    // Chart type
    chart_type = (typeof(chart_type) !== "undefined") ? chart_type : "column";

    // Convert data from custom format to arrays for chart
    var converter = HARSTORAGE.Converter(points);

    var categories = converter.categories,
        yAxis = converter.yAxis,
        series = converter.series;

    // Chart Object
    new Highcharts.Chart({
        chart: {
            renderTo: "chart",
            defaultSeriesType: chart_type
        },
        credits: {
            enabled: false
        },
        // exporting: {
        //     buttons: {
        //         printButton: {
        //             enabled: false
        //         },
        //         exportButton: {
        //             menuItems: [
        //                 {},
        //                 null,
        //                 null,
        //                 {}
        //             ]
        //         }
        //     },
        //     url: "/chart/export",
        //     filename: "superposed",
        //     width: 1200
        // },
        title: {
            text: "Performance Trends"
        },
        xAxis: [{
            categories: categories,
            tickInterval: Math.ceil(categories.length / 10),
            tickmarkPlacement: "on"
        }],
        yAxis: yAxis,
        tooltip: {
            formatter: function() {
                var unit = HARSTORAGE.Units[this.series.name];
                return "<b>" + this.y + " " + unit + "</b>" + " (" + this.x + ")";
            }
        },
        plotOptions: {
            series: {
                cursor: "pointer",
                events: {
                    hide: function() {
                        this.yAxis.axisTitle.hide();
                    },
                    show: function() {
                        this.yAxis.axisTitle.show();
                    }
                }
            }
        },
        series: series
    });
};

/*
 * Test results
 */
HARSTORAGE.RunInfo = function(series) {
    "use strict";

    this.series = series;
    this.timeline = null;

    // Pointer
    var that = this;

    // Initialize cache
    this.cache = {};

    this.visible = false;

    // Add event handler to selector box
    var run_timestamp = document.getElementById("run_timestamp");

    run_timestamp.onchange = function() {
        that.get(that.series, $(this).val(), undefined, that.timeline.multi_har_idx);
    };

};

HARSTORAGE.RunInfo.prototype.set_timeline = function (timeline) {
    this.timeline = timeline;
};

//Page Resources
HARSTORAGE.RunInfo.prototype.resources = function (div, title, hash, units, width) {
    "use strict";

    // Extract data
    var data  = [];

    for (var key in hash) {
        if (hash.hasOwnProperty(key)) {
            data.push( [key, hash[key] ]);
        }
    }

    // Chart object
    new Highcharts.Chart({
        chart: {
            renderTo: div,
            defaultSeriesType: "pie",
            plotBackgroundColor: null,
            plotBorderWidth: null,
            plotShadow: false,
            width: width,
            height: 300
        },
        credits: {
            enabled: false
        },
        // exporting: {
        //     buttons: {
        //         printButton: {
        //             enabled: false
        //         },
        //         exportButton: {
        //             menuItems: [
        //                 {},
        //                 null,
        //                 null,
        //                 {}
        //             ]
        //         }
        //     },
        //     url:"/chart/export",
        //     filename: "resources",
        //     width: width
        // },
        title: {
            text: title
        },
        tooltip: {
            formatter: function() {
                return "<b>"+ this.point.name +"</b>: "+ this.y + units;
            }
        },
        plotOptions: {
            series: {
                showInLegend: true
            }
        },
        series: [{
            data: data
        }]
    });
};

//Page Speed details
HARSTORAGE.RunInfo.prototype.pagespeed = function (pagespeed) {
    "use strict";

    // Spliting data for chart
    var rules   = ["Total Score"],
        scores  = [pagespeed["Total Score"]];

    for (var rule in pagespeed) {
        if (pagespeed.hasOwnProperty(rule) && rule !== "Total Score") {
            rules.push(rule);
            scores.push(pagespeed[rule]);
        }
    }

    // Chart height
    var height = Math.max(75 + 20 * rules.length, 100);

    // Chart object
    new Highcharts.Chart({
        chart: {
            renderTo: "pagespeed",
            defaultSeriesType: "bar",
            height: height,
            width: 930
        },
        credits: {
            enabled: false
        },
        // exporting: {
        //     buttons: {
        //         printButton: {
        //             enabled: false
        //         },
        //         exportButton: {
        //             enabled: false
        //         }
        //     }
        // },
        title: {
            text: "Page Speed Scores"
        },
        xAxis: {
            title: {
                text: null
            },
            categories: rules,
            labels: {
                formatter: function() {
                    if (this.value === "Total Score") {
                        return "<b>@" + this.value + "</b>";
                    } else {
                        return this.value;
                    }
                }
            }
        },
        yAxis: {
            title: {
                text: null
            },
            min: 0,
            max: 105,
            endOnTick: false
        },
        tooltip: {
            formatter: function() {
                return this.x +": "+ this.y;
            }
        },
        plotOptions: {
            bar: {
                dataLabels: {
                    enabled: true
                }
            },
            series: {
                showInLegend: false,
                animation: false
            }
        },
        series: [{
            data: scores
        }]
    });
};

//Get data for Run Info
HARSTORAGE.RunInfo.prototype.get = function(series, single_ts, aggregated_ts, multi_har_idx) {
    "use strict";

    if (!single_ts && !aggregated_ts) {
        single_ts = this.last_single_ts;
        aggregated_ts = this.last_aggregated_ts;
        if (!single_ts && !aggregated_ts) return;
    } else {
        this.last_single_ts = single_ts;
        this.last_aggregated_ts = aggregated_ts;
    };

    // Pointer
    var that = this;

    // Dynamic data
    this.json = [];

    // Show Ajax http://localhost:5000/results/timeline?label=http%3A%2F%2Fwww.idealo.de%2Fpreisvergleich%2FProductCategory%2F4012.html&mode=url&from=2013-03-06%2015:18:52&to=2013-03-06%2016:10:22&navi=allsspinner
    $("#spinner").show();
    $("#run_info_box_empty").hide();
    $("#run_info_box").show();
    this.visible = true;

    // Formatter
    this.formatter = function(value, units) {
        // Default units
        if (typeof(units) === "undefined") {
            units = "";
        }

        // Formatter
        switch ( typeof(value) ) {
        case "number":
            if (value >= 1000) {
                var seconds = Math.floor(value/1000);
                var milliseconds = value - seconds*1000;

                if (milliseconds < 10) {
                    milliseconds = "00" + milliseconds;
                } else if (milliseconds < 100) {
                    milliseconds = "0" + milliseconds;
                }

                return seconds + " " +  milliseconds + " " + units;
            } else {
                return value + " " + units;
            }
            break;
        case "string":
            return value;
        default:
            return "n/a";
        }
    };

    // Update test results
    var set_data = function() {
        that.json = JSON.parse(that.xhr.responseText);

        // Update cache
        // if (typeof(that.cache[that.URI]) === "undefined") {
        //     that.cache[that.URI] = that.json;
        // }

        if (that.json.no_data) {
            return;
        };

        // populate the timestamp-select-box with the results
        if (that.json.timestamps.length) {
            var options = $("#run_timestamp");
            options.empty();
            $.each(that.json.timestamps, function() {
                options.append($("<option />").val(this).text(this));
            });
            options.trigger("liszt:updated");
        };

        // Summary
        $("#full-load-time").html(that.formatter(that.json.summary.full_load_time, "ms"));
        $("#transaction-time").html(that.formatter(that.json.summary.transaction_time, "ms"));
        $("#onload-event").html(that.formatter(that.json.summary.onload_event, "ms"));
        $("#start-render-time").html(that.formatter(that.json.summary.start_render_time, "ms"));
        $("#time-to-first-byte").html(that.formatter(that.json.summary.time_to_first_byte, "ms"));

        $("#total-dns-time").html(that.formatter(that.json.summary.total_dns_time, "ms"));
        $("#total-transfer-time").html(that.formatter(that.json.summary.total_transfer_time, "ms"));
        $("#total-server-time").html(that.formatter(that.json.summary.total_server_time, "ms"));
        $("#avg-connecting-time").html(that.formatter(that.json.summary.avg_connecting_time, "ms"));
        $("#avg-blocking-time").html(that.formatter(that.json.summary.avg_blocking_time, "ms"));

        $("#total-size").html(that.formatter(that.json.summary.total_size, "kB"));
        $("#text-size").html(that.formatter(that.json.summary.text_size, "kB"));
        $("#media-size").html(that.formatter(that.json.summary.media_size, "kB"));
        $("#cache-size").html(that.formatter(that.json.summary.cache_size, "kB"));

        $("#requests").html(that.formatter(that.json.summary.requests));
        $("#redirects").html(that.formatter(that.json.summary.redirects));
        $("#bad-requests").html(that.formatter(that.json.summary.bad_requests));
        $("#domains").html(that.formatter(that.json.summary.domains));

        // HAR Viewer
        var iframe  = document.createElement("iframe");
        var url = "/results/harviewer?inputUrl=/results/download%3Fid%3D";
            url += that.json.har;
            url += "&expand=true";

        iframe.setAttribute("src", url);
        iframe.setAttribute("width", "940");
        iframe.setAttribute("id", "harviewer-iframe");
        iframe.setAttribute("frameBorder", "0");
        iframe.setAttribute("frameBorder", "0");
        iframe.setAttribute("scrolling", "no");

        $("#harviewer").html(iframe);

        window.setInterval("HARSTORAGE.autoHeight()", 100);

        // New tab feature of HAR Viewer
        var newtab = document.getElementById("newtab");

        newtab.onclick = function () {
            window.open(url);
        };

        // Resources by Size
        setTimeout(
            function() {
                that.resources("by-size", "Resources by Size", that.json.weights, " kB", 450);
            },
            50
        );

        // Resources by Requests
        setTimeout(
            function() {
                that.resources("by-req", "Resources by Requests", that.json.requests, "", 450);
            },
            150
        );

        // Resources by Time
        setTimeout(
            function() {
                that.resources("by-time", "Resources by Time", that.json.res_time, "", 450);
            },
            150
        );

        // Domains by Size
        setTimeout(
            function() {
                that.resources("domains-by-size", "Domains by Size", that.json.d_weights, " kB", 930);
            },
            250
        );

        // Domains by Requests
        setTimeout(
            function() {
                that.resources("domains-by-req", "Domains by Requests", that.json.d_requests, "", 930);
            },
            350
        );

        // Page Speed Details
        setTimeout(
            function() {
                that.pagespeed(that.json.pagespeed);
            },
            450
        );

        // Screenshot
        if (that.json.screenshot_url) {
            $("#screenshot").html("<img src='" + that.json.screenshot_url + "'>");
        } else {
            $("#screenshot").html("Sorry, no screenshot!");
        };

        screenshot_newtab.onclick = function () {
            window.open(that.json.screenshot_url);
        };


        // Hide Ajax spinner
        $("#spinner").fadeOut();
    };

    // Request data via XHR or read from cache
    if (aggregated_ts) {
        this.URI = "runinfo?series=" + series + "&ag_timestamp=" + encodeURIComponent(aggregated_ts);
    } else {
        this.URI = "runinfo?series=" + series + "&timestamp=" + encodeURIComponent(single_ts);
    };

    if (multi_har_idx != undefined && multi_har_idx != null) {
        this.URI += "&mhidx=" + multi_har_idx;
    };

    this.xhr = new XMLHttpRequest();

    this.xhr.onreadystatechange = function() {
        if (this.readyState === 4 && this.status === 200) {
            set_data();
        }
    };

    this.xhr.open("GET", this.URI, true);
    this.xhr.send();
};

//Get data for Run Info
HARSTORAGE.RunInfo.prototype.get_multi_har_info = function(series, single_ts, aggregated_ts, multi_har_idx) {
    "use strict";

    var that = this;

    // Dynamic data
    this.json = [];

    $("#spinner").show();
    $("#run_info_box_empty").show();
    $("#run_info_box").hide();


    var xhr = new XMLHttpRequest();

    // Update test results
    var set_data = function() {
        var json = JSON.parse(xhr.responseText);

        if (json.no_data) {
            return;
        };

        if (json.multi_har_info_str) {
            var multi_har_info = json.multi_har_info_str.split("|");
            that.draw_multi_har_info(multi_har_info, null);
        };

        // Hide Ajax spinner
        $("#spinner").fadeOut();
    };

    var uri = "";

    // Request data via XHR or read from cache
    if (aggregated_ts) {
        uri = "multi_har_info?series=" + series + "&ag_timestamp=" + encodeURIComponent(aggregated_ts);
    } else {
        uri = "multi_har_info?series=" + series + "&timestamp=" + encodeURIComponent(single_ts);
    };

    if (multi_har_idx != undefined && multi_har_idx != null) {
        uri += "&mhidx=" + multi_har_idx;
    };

    xhr.onreadystatechange = function() {
        if (this.readyState === 4 && this.status === 200) {
            set_data();
        }
    };

    xhr.open("GET", uri, true);
    xhr.send();
};


HARSTORAGE.RunInfo.prototype.draw_multi_har_info = function (multi_har_info) {

    var that = this;


    $("#multi_har_info").html("");
    if (multi_har_info) {


        var el = $("<div>Show<br>combined<br>timings</div>")
        el.appendTo("#multi_har_info");
        el.css({"cursor": "pointer",
                "color": "gray",
                "font-family": "sans-serif",
                "font-style": "italic",
                "font-size": "11px",
                "border": "1px solid transparent",
                "padding": "2px",
                "width": "100px",
                "float": "left",
                "text-align": "center"});

        el.click(function() {
            $("#multi_har_info > *").each(function() {
                $(this).attr("_selected", "false");
                update_screenshot_border($(this));
            });
            on_multi_har_screenshot_click(null);
        });

        var selected_idx = multi_har_info.splice(0, 1);
        for (var idx in multi_har_info) {
            var split_pos = multi_har_info[idx].indexOf("#");
            var tmp = multi_har_info[idx].substr(0, split_pos).split("*");
            var info_string = multi_har_info[idx].substring(split_pos + 1);
            var screenshot_url = tmp[0];
            var img_info = tmp[1];
            var img_bar = parseInt(tmp[2]);
            var el = $("<img>").attr({"src": screenshot_url, "width": 100, "title": info_string, "idx": idx, "height": 100});
            var is_error = false;
            if (info_string.indexOf("Error:") >= 0) { is_error = true };
            el.attr("_selected", "false");
            if (idx == selected_idx) {
                el.attr("_selected", "true");
            };
            if (is_error) {
                el.attr("_error", "true");
            };
            update_screenshot_border(el);
            el.appendTo("#multi_har_info");
            el.click(function() {
                var idx = $(this).attr("idx");
                $("#multi_har_info > *").each(function() {
                    $(this).attr("_selected", "false");
                });
                $(this).attr("_selected", "true");
                $("#multi_har_info > *").each(function() {
                    update_screenshot_border($(this));
                });
                on_multi_har_screenshot_click(idx);
            });
            $("<span>").css({"position": "absolute",
                             "margin-left": -95,
                             "background": "red",
                             "width": 7,
                             "height": img_bar,
                             "margin-top": 80 - img_bar}).appendTo("#multi_har_info");
            $("<span>" + img_info + "</span>").css({"position": "absolute",
                                                    "margin-left": -95,
                                                    "background": "white",
                                                    "color": "black",
                                                    "margin-top": 80,
                                                    "font-size": "10px",
                                                    "font-weight": "bold",
                                                    "font-family": "courier"}).appendTo("#multi_har_info");
        };

        $("#multi_har_info > *").hover(function(){
            $(this).css({"border": "1px solid pink", "border-radius": "5px"});
            create_screenshot_preview($(this).attr("src"), $(this).offset());
            if ($(this).attr("_error") == "true") {
                $(this).css({"border-bottom": "3px dotted red"});
            };
          },function(){
            delete_screenshot_preview();
            update_screenshot_border($(this));
        });
    };

    function update_screenshot_border(el) {
        if (el.attr("_selected") == "true") {
            el.css({"border": "1px solid red", "border-radius": "5px"});
        } else {
            el.css({"border": "1px solid transparent"});
        };
        if (el.attr("_error") == "true") {
            el.css({"border-bottom": "3px dotted red",  "border-radius": "0px"});
        };
    };

    function on_multi_har_screenshot_click(idx) {

        that.timeline.get(that.series,
                          undefined,
                          undefined,
                          undefined,
                          idx);

        if (idx != null) {
            that.get(that.series, undefined, undefined, idx);
        } else {
            $("#run_info_box_empty").show();
            $("#run_info_box").hide();
        };

    };


    function create_screenshot_preview(url, pos) {
        $("#sspreview").remove();

        if (that.visible) return;

        body_scrolled = false;

        if (!url) return;

        var el = $("<div id='sspreview'>");
        el.css({"position": "absolute", "background": "white"});
        el.offset({"top": pos.top - 350, "left": pos.left + 120});
        var img = $("<img width='50%'>").attr("src", url);
        img.css({"border": "2px solid gray", "padding": "2px"});
        img.appendTo(el);
        el.appendTo("body");

        img.mouseover(function() {
            $("#sspreview").remove();
        });
    };

    function delete_screenshot_preview() {
        if (!body_scrolled) {
            $("#sspreview").remove();
        };
    };

};


// Add delay for async rendering
HARSTORAGE.RunInfo.prototype.changeVisibility = function () {
    "use strict";

    var newtab_btn  = document.getElementById("newtab");
    var screenshot_newtab_btn  = document.getElementById("screenshot_newtab");

    newtab_btn.style.display    = "inline";
    screenshot_newtab_btn.style.display    = "inline";
};

HARSTORAGE.RunInfo.prototype.timedStyleChange = function () {
    "use strict";

    setTimeout(this.changeVisibility, 1000);
};

HARSTORAGE.RunInfo.prototype.addSpinner = function() {
    "use strict";

    // Spinner object
    this.spinner = document.getElementById("spinner");
    new Spinner(HARSTORAGE.SpinnerOpts).spin(this.spinner);
};

/*
 * Auto Height module
 */
HARSTORAGE.autoHeight = function() {
    "use strict";

    var iframe = document.getElementById("harviewer-iframe");
    iframe.height = iframe.contentDocument.body.offsetHeight;
};

/*
 * Aggregated Statistics
 */
HARSTORAGE.AggregatedStatistics = function(id) {
    "use strict";

    // Determine metric type from Query string
    var metric,
        href;

    if (location.href.indexOf("metric") === -1) {
        href = location.href + "&metric=";
        metric = "Average";
    } else {
        href = location.href.split("metric")[0] + "metric=";
        metric = location.href.split("metric")[1].split("=")[1];

        if (metric === "90th%20Percentile") {
            metric = "90th Percentile";
        }
    }

    // Update selector box active option
    var selector = document.getElementById(id);

    for (var i = 0, len = selector.options.length; i < len; i += 1 ) {
        if (selector.options[i].value === metric) {
            selector.selectedIndex = i;
            $("#" + id).trigger("liszt:updated");
            break;
        }
    }

    // Add event handler to selector box
    selector.onchange = function() {
        location.href = href + this.value;
    };
};

/*
 * Superpose Form
 */
HARSTORAGE.SuperposeForm = function() {
    "use strict";

    // Pointer
    var that = this;

    // Initialize cache
    this.cache = {};

    // Select box event handler
    var selector = document.getElementById("step_1_label");
    selector.onchange = function() {
        that.setTimestamps(this.name);
    };

    // Submit button event handler
    var submit = document.getElementById("submit");
    submit.onclick = function() {
        return that.submit();
    };

    // Add button event handler
    var add = document.getElementById("step_1_add");
    add.onclick = function() {
        that.add(this);
    };

    // Delete button event handler
    var del = document.getElementById("step_1_del");
    del.onclick = function() {
        that.del(this);
    };
    del.style.display = "none";

    // Chart options
    var checkbox = document.getElementById("column");
    checkbox.onclick = function() {
        that.checkbox(this);
    };

    checkbox = document.getElementById("spline");
    checkbox.onclick = function() {
        that.checkbox(this);
    };
};

// Form validation
HARSTORAGE.SuperposeForm.prototype.submit = function() {
    "use strict";

    var selectors = document.getElementsByTagName("select");

    for (var i = 0, len = selectors.length/3; i < len; i += 1) {
        var id = 1 + i*3;

        var start_ts    = selectors.item(id).options[ selectors.item(id).options.selectedIndex ].value;
        var end_ts      = selectors.item(id+1).options[ selectors.item(id+1).options.selectedIndex ].value;

        if (end_ts < start_ts) {
            window.alert("Invalid timestamps!");
            return false;
        }
    }

    var form = document.getElementById("superpose-form");
    form.onsubmit = "return true;";

    return true;
};

// Add new step
HARSTORAGE.SuperposeForm.prototype.add = function(button) {
    "use strict";

    var i,
        len,
        prev_button;

    var that = this;

    // Find previous and new id
    var prev_id = button.id.split("_")[0] + "_" + button.id.split("_")[1],
        new_id = prev_id.split("_")[0] + "_" + ( parseInt ( prev_id.split("_")[1], 10) +1 );

    // Add new line to container
    var prev_div = document.getElementById(prev_id),
        new_div = prev_div.cloneNode(true);

    new_div.setAttribute("id", new_id);

    var container = document.getElementById("container");
    container.appendChild(new_div);

    // Update name and id of selectors
    var selectors = new_div.getElementsByTagName("select");

    for (i = selectors.length; i -- ; ) {
        switch (selectors.item(i).name) {
        case prev_id + "_label":
            selectors.item(i).name  = new_id + "_label";
            selectors.item(i).id    = new_id + "_label";
            selectors.item(i).onchange = function() {
                that.setTimestamps(this.name);
            };
            break;
        case prev_id + "_start_ts":
            selectors.item(i).name  = new_id + "_start_ts";
            selectors.item(i).id    = new_id + "_start_ts";
            break;
        case prev_id + "_end_ts":
            selectors.item(i).name  = new_id + "_end_ts";
            selectors.item(i).id    = new_id + "_end_ts";
            break;
        default:
            break;
        }
    }

    // Update inputs
    var inputs = new_div.getElementsByTagName("input");

    for (i = 0, len = inputs.length; i < len; i += 1) {
        switch (inputs.item(i).id) {
        case prev_id + "_add":
            // Set new id
            inputs.item(i).id = new_id + "_add";

            // Hide previous button
            prev_button = document.getElementById(prev_id + "_add");
            prev_button.style.display = "none";

            // Set event handler
            inputs.item(i).onclick = function() {
                that.add(this);
            };
            break;
        case prev_id + "_del":
            // Set new id
            inputs.item(i).id = new_id + "_del";

            // Hide previous button
            prev_button = document.getElementById(prev_id + "_del");
            prev_button.style.display = "none";

            // Show current button
            inputs.item(i).style.display = "inline";

            // Set event handler
            inputs.item(i).onclick = function() {
                that.del(this);
            };
            break;
        default:
            break;
        }
    }
    // Update head
    var divs = new_div.getElementsByTagName("div");

    for (i = 0, len = divs.length; i < len; i += 1) {
        if (divs.item(i).id === prev_id + "_head" ) {
            // New id
            divs.item(i).id = new_id + "_head";

            // New label
            divs.item(i).innerHTML = "Set " + new_id.split("_")[1] + " &gt;";
        }
    }

    // Update timestamp
    this.setTimestamps(new_id + "_label");
};

// Delete selected step
HARSTORAGE.SuperposeForm.prototype.del = function(button) {
    "use strict";

    var prev_button;

    // Calculate id
    var id = button.id.split("_")[0] + "_" + button.id.split("_")[1],
        prev_id = button.id.split("_")[0] + "_" + (parseInt( button.id.split("_")[1], 10) - 1);

    // Get DIVs
    var div = document.getElementById(id),
        container = document.getElementById("container");

    // Delete current line
    container.removeChild(div);

    // Show previous button
    prev_button = document.getElementById(prev_id + "_add");
    prev_button.style.display = "inline";

    if (prev_id !== "step_1") {
        prev_button = document.getElementById(prev_id + "_del");
        prev_button.style.display = "inline";
    }
};

// Set timelines for selected label
HARSTORAGE.SuperposeForm.prototype.setTimestamps = function(id) {
    "use strict";

    // Poiner
    var that = this;

    // Dynamic data
    this.dates = [];

    // Show Ajax spinner
    $("#spinner").show();

    // Update timestamps
    var set_data = function() {
        var i,
            len,
            ts;

        // Calculate id
        id  = id.split("_")[0] + "_" + id.split("_")[1];

        // Hide Ajax spinner
        $("#spinner").fadeOut();

        // Update cache
        if (typeof(that.cache[that.URI]) === "undefined") {
            that.dates = that.xhr.responseText.split(";");
            that.cache[that.URI] = that.dates;
        } else {
            that.dates.reverse();
        }

        // Start timestamps
        var select = document.getElementById(id + "_start_ts");
        select.options.length = 0;

        for (i = 0, len = that.dates.length; i < len; i += 1) {
            ts = that.dates[i];
            select.options[i] = new Option(ts, ts, false, false);
        }

        // End timestamps
        select = document.getElementById(id + "_end_ts");
        select.options.length = 0;
        that.dates.reverse();

        for (i = 0, len = that.dates.length; i < len; i += 1) {
            ts = that.dates[i];
            select.options[i] = new Option(ts, ts, false, false);
        }
    };

    // Request data via XHR or read from cache
    var select = document.getElementById(id);
    var label = select.options[select.selectedIndex].text;
    this.URI = "dates?label=" + label;

    this.xhr = new XMLHttpRequest();

    this.xhr.onreadystatechange = function() {
        if (this.readyState === 4 && this.status === 200) {
            set_data();
        }
    };

    if (typeof(this.cache[this.URI]) === "undefined") {
        this.xhr.open("GET", this.URI, true);
        this.xhr.send();
    } else {
        this.dates = this.cache[this.URI];
        set_data();
    }
};
// Add Ajax spinner
HARSTORAGE.SuperposeForm.prototype.addSpinner = function() {
    "use strict";

    this.spinner = document.getElementById("spinner");
    new Spinner(HARSTORAGE.SpinnerOpts).spin(this.spinner);
};

// Checkbox group
HARSTORAGE.SuperposeForm.prototype.checkbox = function(input) {
    "use strict";

    var id1  = "spline",
        id2  = "column",
        id;

    if (input.checked) {
        id = (input.id === id1) ? id2 : id1;
        var checkbox = document.getElementById(id);
        checkbox.checked = false;
    }
};
